/*
 * @(#)AbstractClassBuilder.java 1.0 2009/12/01
 */
package stardust.core.java.compiler;


import java.io.File;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import stardust.core.io.IOUtils;
import stardust.core.java.DynamicClassLoader;
import stardust.core.log.Log;
import stardust.core.support.Sequence;


/**
 * <code>AbstractClassBuilder</code> 类实现 Java 的动态编译与加载。 在调用 <tt>compile()</tt>
 * 方法编译后通过 <tt>getClassLoader()</tt> 方法加载编译后的类。 示例程序：
 * <code>
 * if (ClassBuilder.compile())
 * {
 *     ClassBuilder.getClassLoader().loadClass("xiaoyuan.DynamicClass");
 * }
 * </code>
 * <p/>
 * @version 1.0, 2009/12/01
 * @author ChenChang
 */
public abstract class AbstractClassBuilder
{

  /**
   * 日志记录器。
   */
  protected Log log = new Log(this.getClass());

  /**
   * 动态
   * <code>ClassLoader</code> 类。
   */
  protected DynamicClassLoader classLoader;

  /**
   * 临时编译输出流。
   */
  protected Writer BLANK_PRINTWRITER = new PrintWriter(IOUtils.BLANK_OUTPUTSTREAM);

  /**
   * 目标生成目录。
   */
  protected File targetDirectory;

  /**
   * 基于目标生成目录的临时目录。
   */
  protected File classesDirectory;

  /**
   * 虚拟 Java 源程序文件。
   */
  protected Map<String, String> files = new HashMap<String, String>();

  /**
   * 编译参数。
   */
  protected List<String> args = new ArrayList<String>();

  /**
   * 序列生成器。
   */
  protected static final Sequence sequence = new Sequence();

  /**
   * 通过限定的目录创造一个 <tt>ClassBuilder</tt> 类。
   * <p/>
   * @param targetDirectory
* 目标生成目录。
   */
  public AbstractClassBuilder(File targetDirectory)
  {
    this.classLoader = new DynamicClassLoader();
    this.targetDirectory = targetDirectory;

    this.classesDirectory = new File(this.targetDirectory, this.getClass().getName() + "$" + AbstractClassBuilder.sequence.next());
    this.classesDirectory.mkdirs();
    this.classLoader.addClasspath(this.classesDirectory);

    this.args.add("-g:none"); // 不生成任何调试信息
    this.args.add("-nowarn"); // 不生成任何警告
    this.args.add("-d");
    this.args.add(URLDecoder.decode(classesDirectory.getAbsolutePath()));
    this.args.add("-classpath");
    this.args.add(URLDecoder.decode(System.getProperty("java.class.path")) + ";" + URLDecoder.decode(System.getProperty("java.library.path")) + ";" + URLDecoder.decode(Thread.currentThread().getContextClassLoader().getResource("").getFile()));
  }

  /**
   * 增加一段需要编译的 Java 源程序。
   * <p/>
   * @param className
* 全限定的类名称。
   * @param source
   * 源程序代码。
   */
  public void add(String className, String source)
  {
    this.files.put(className, source);
  }

  /**
   * 通过全限定类名移除一段源程序。
   * <p/>
   * @param className
* 全限定的类名称。
   */
  public void remove(String className)
  {
    this.files.remove(className);
  }

  /**
   * 移除全部源程序。
   */
  public void clear()
  {
    this.files.clear();
  }

  /**
   * 立刻执行编译动作。
   * <p/>
   * @return 编译成功标志。
   */
  public abstract boolean compile();

  /**
   * 删除编译产生的临时文件。
   * <p/>
   * @return 全部删除成功返回 <tt>true</tt> 值。
   */
  public boolean delete()
  {
    return IOUtils.delete(this.classesDirectory);
  }

  /**
   * 在虚拟机退出时删除编译产生的临时文件。
   */
  public void deleteOnExit()
  {
    IOUtils.deleteOnExit(this.classesDirectory);
  }

  /**
   * 获取编译的基础目录。
   * <p/>
   * @return 编译文件的基础目录。
   */
  public File getTargetDirectory()
  {
    return this.targetDirectory;
  }

  /**
   * 获取以基础目录为基准的真实编译路径。
   * <p/>
   * @return 编译后 class 文件存放的目录。
   */
  public File getClassesDirectory()
  {
    return this.classesDirectory;
  }

  /**
   * 获取当前 <tt>ClassBuilder</tt> 的类加载器。
   * <p/>
   * @return 动态类加载器。
   */
  public DynamicClassLoader getClassLoader()
  {
    return classLoader;
  }

}
